package com.ibeetl.eval;

import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.beetl.core.Configuration;
import org.beetl.core.GroupTemplate;
import org.beetl.core.exception.ScriptEvalError;
import org.beetl.core.resource.StringTemplateResourceLoader;
import org.jdom2.CDATA;
import org.jdom2.Content;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
/**
 * 目前不具备保留中间结算结果到TreeNode，导致不能互相引用
 * @author xiandafu
 *
 */
public class EvalEngine {
	static GroupTemplate gt = null;
	static {
		 try {
			gt = new GroupTemplate(new StringTemplateResourceLoader(),Configuration.defaultConfiguration());
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	public TreeNode run(Reader reader,Map<String,Object> paras) throws Exception{
		Document doc = getXML(reader);
		Element workspaceEle = doc.getRootElement();
		Element projectEle = workspaceEle.getChild("project");
		Map defineVars = getProjectVarDefine(projectEle);
		Map ctxMap = this.merage(paras, defineVars);
		//评级描述
		Element docEle = projectEle.getChild("doc");
		int type = this.getRuleType(docEle.getAttributeValue("f"));
		
		List<Element> list = docEle.getChildren();
		Object result =  this.processChildren(list, ctxMap, this.getNodeArth(type));
		//TODO,需要返回所有节点计算过程用于查看和调试。目前仅仅返回根节点
		TreeNode node = new TreeNode();
		node.setResult(result);
		node.setNodeRefInfo(docEle.getAttribute("name"));
		return node;
	
		
	}
	
	private Document getXML(Reader reader) throws Exception {
		SAXBuilder jdomBuilder = new SAXBuilder();
		 
        Document jdomDocument = jdomBuilder.build(reader);
        return jdomDocument;
	}
	
	/**
	 * 得到每个评级预先定义的变量
	 * @param projectEle
	 * @return
	 */
	private Map getProjectVarDefine(Element projectEle){
		return Collections.emptyMap();
	}
	
	private Object processChildren(List<Element> list,Map<String,Object> ctxMap,NodeArth nodeArth ) throws ScriptEvalError{
		List<Object> rets = new ArrayList<Object>();
		for(Element rule:list){
			String ruleName = rule.getAttributeValue("name");
			Map newPara = newMap(ctxMap);
			Object object = processRule(rule,newPara);
			ctxMap = this.merage(ctxMap, ruleName,object);
			rets.add(object);
		}
		Object ret = nodeArth.getValue(rets);
		return ret;
		
	}
	
	private Object processRule(Element ruleEle,Map<String,Object> ctxMap) throws ScriptEvalError{
		//变量名字
		String name = ruleEle.getAttributeValue("name");
		int type = getRuleType(ruleEle.getAttributeValue("f"));
		ctxMap = this.newMap(ctxMap);
		switch(type){
			case 0:{
				String express = ruleEle.getText();
				//计算表达式结果
				Object ret = runExpress(express,ctxMap);
				return ret;
				
			}
			case 1: {
				//sum
				List<Element> list = ruleEle.getChildren();
				Object ret = this.processChildren(list, ctxMap,new SumNodeArth());
				return ret;
			}
			case 2:{
				String script = getCDATA(ruleEle);
				Object ret = runScript(script,ctxMap);
				return ret;
			}
			default:{
				throw new RuntimeException(type+" 不支持");
			}
		}
	}
	
	
	
	/**
	 * 直接运行脚本，得出结果
	 * @param script
	 * @param paras
	 * @return
	 */
	private Object runExpress(String script,Map<String,Object> paras) throws ScriptEvalError{
		String express = "return "+script+";";
		try {
			Map map= gt.runScript(express, paras);
			return map.get("return");
		} catch (ScriptEvalError e) {
			throw e ;
		}
		
	}
	
	private Object runScript(String script,Map<String,Object> paras) throws ScriptEvalError{
		try {
			Map map= gt.runScript(script, paras);
			return map.get("return");
		} catch (ScriptEvalError e) {
			throw e ;
		}
		
	}
	
	private int getRuleType(String type){
		if(type==null){
			return  0;
		}else{
			if(type.equals("sum")){
				return 1;
			}else if(type.equals("script")){
				return 2;
			}
			else{
				throw new RuntimeException(type+" 不支持");
			}
		}
	}
	
	private Map<String,Object> merage(Map<String,Object> para,String newName,Object newValue){
		Map<String,Object> newMap = new HashMap<String,Object>();
		newMap.putAll(para);
		newMap.put(newName, newValue);
		return newMap;
	}
	private Map<String,Object> merage(Map<String,Object> para,Map map){
		Map<String,Object> newMap = new HashMap<String,Object>();
		//todo 变量已经定义
		newMap.putAll(para);
		newMap.putAll(map);
		return newMap;
	}
	
	private Map<String,Object> newMap(Map<String,Object> para){
		Map<String,Object> newMap = new HashMap<String,Object>();
		
		newMap.putAll(para);
		return newMap;
	}
	
	private NodeArth getNodeArth(int type){
		return new SumNodeArth();
	}
	
	private String getCDATA(Element e){
		for(Content c : e.getContent()){
			 if (c instanceof CDATA) {  
                CDATA cdata = (CDATA) c;  
                return cdata.getText();
            } 
		}
		return null;
	}
	
}
